package client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import simpleAI.SimpleAI;
import utils.BetState;
import utils.Color;
import utils.Constants;
import utils.InitState;
import utils.Poker;
import AdvancedAI.SelectAI;

public class GameClient {
    private Socket socket = null;
    private BufferedReader input = null;
    private PrintWriter output = null;
    private String playerID;
    private int handNum = 0;
    private SimpleAI ai;
//    private AdvancedAI ai;
//    private SelectAI ai;
    private String needNotify;
    
    public GameClient(String serverIp, int serverPort, String clientIp, 
            int clientPort, String playerID, boolean needNotify) {
        boolean flag = false;
        while (!flag) {
            try {
                socket = new Socket(); //此时Socket对象未绑定到本地端口，并且未连接远程服务器
                socket.setReuseAddress(true);

                SocketAddress localAddr = new InetSocketAddress(clientIp, clientPort);
                SocketAddress remoteAddr = new InetSocketAddress(serverIp, serverPort);

                socket.bind(localAddr); //与本地端口绑定
                
                socket.connect(remoteAddr, 200);
                flag = true;
                System.out.println("success!");
                input = new BufferedReader(new InputStreamReader(
                        socket.getInputStream()));
                output = new PrintWriter(socket.getOutputStream(), true);
                
            } catch (Exception e) {
                System.out.println("connect failed,try again!");
                try {
                    socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                e.printStackTrace();
            }
        }
        
        this.playerID = playerID;
        if (needNotify) {
            this.needNotify = "need_notify ";
        }
        else {
            this.needNotify = "";
        }
    }
    
    /**
     * 外部调用该函数，开始游戏
     * @throws Exception
     */
    public void startGame() throws Exception {
        this.register();
//        this.ai = new AdvancedAI(playerID);
        this.ai = new SimpleAI(playerID);
//        this.ai = new SelectAI(playerID);
        
        while (true) {
            // 从服务端读取数据
            ArrayList<String> inputData = this.readData();
            
            for (int i = 0; i < inputData.size(); i++) {
                System.out.println(inputData.get(i));
            }
            
            if (inputData.get(0).equals("game-over")) {
                this.gameOver();
                break;
            }
            else if (inputData.get(0).equals("seat")) {
                this.handNum ++;
                this.setSeatInfo(inputData.get(1));
            }
            else if (inputData.get(0).equals("blind")) {
                this.setBlindInfo(inputData.get(1));
            }
            else if (inputData.get(0).equals("hold")) {
                this.setHoldInfo(inputData.get(1));
            }
            else if (inputData.get(0).equals("inquire")) {
                this.inquireAndAction(inputData.get(1));
            }
            else if (inputData.get(0).equals("flop")) {
                this.setFlopInfo(inputData.get(1));
            }
            else if (inputData.get(0).equals("turn")) {
                this.setTurnInfo(inputData.get(1));
            }
            else if (inputData.get(0).equals("river")) {
                this.setRiverInfo(inputData.get(1));
            }
            else if (inputData.get(0).equals("showdown")) {
                this.showdown(inputData.get(1));
            }
            else if (inputData.get(0).equals("pot-win")) {
                this.potwin(inputData.get(1));
            }
            else if (inputData.get(0).equals("notify")) {
                this.setNotifyInfo(inputData.get(1));
            }
        }
    }
    

    /**
     * 向服务端注册
     */
    private void register() {
        try {
            this.sendData("reg: " + this.playerID + " wukong " + 
                    this.needNotify + "\n");
        }
        catch (Exception e) {
            System.out.println(e);
        }
    }
    
    /**
     * 游戏结束，关闭客户端
     */
    private void gameOver() {
        if (this.socket != null) {
            try {
                this.socket.close();
            }
            catch (Exception e) {
                System.out.println(e);
            }
            this.socket = null;
        }
    }
    
    /**
     * 设置座位信息
     * @param msg
     */
    private void setSeatInfo(String msg) {
        String lines[] = msg.split("\n");
        ArrayList<InitState> states = new ArrayList<InitState>();
        
        int jet = 0, mon = 0, pos = 0;
        for (int i = 0; i < lines.length; i++) {
            String line = lines[i];
            if (line.length() > 3) {
                String items[];
                if (line.indexOf(":") != -1) {
                    items = (line.split(": ")[1]).split(" ");
                }
                else {
                    items = line.split(" ");
                }
                String playerID = items[0];
                int jetton = Integer.parseInt(items[1]);
                int money = Integer.parseInt(items[2]);
                states.add(new InitState(playerID, jetton));
                if (playerID.equals(this.playerID)) {
                    jet = jetton;
                    mon = money;
                    pos = i + 1;
                }
            }
        }

        this.ai.setInitStates(states);
        this.ai.setInitInfo(jet, mon, lines.length, pos, this.handNum);
    }
    
    /**
     * 设置盲注信息
     * @param msg
     */
    private void setBlindInfo(String msg) {
        String lines[] = msg.split("\n");
        int blind = 0;
        for (String line: lines) {
            if (line.length() > 3) {
                String items[] = line.split(" ");
                String pid = items[0].substring(0, items[0].length() - 1);
                int jet = Integer.parseInt(items[1]);
                ai.postBlind(pid, jet);
                if (jet > blind) {
                    blind = jet;
                }
            }
        }
        ai.setBlind(blind);
    }
    
    /**
     * 每个玩家分发2张底牌
     * @param msg
     */
    private void setHoldInfo(String msg) {
        String lines[] = msg.split("\n");
        Poker poker[] = new Poker[2];
        for (int i = 0; i < 2; i++) {
            String items[] = lines[i].split(" ");
            poker[i] = this.convertToPoker(items[0], items[1]);
        }
        
        this.ai.addHoldPokers(poker[0], poker[1]);
    }
    
    /**
     * 翻开三张共牌
     * @param msg
     */
    private void setFlopInfo(String msg) {
        String lines[] = msg.split("\n");
        Poker poker[] = new Poker[3];
        for (int i = 0; i < 3; i++) {
            String items[] = lines[i].split(" ");
            poker[i] = this.convertToPoker(items[0], items[1]);
        }
        
        this.ai.addFlopPokers(poker[0], poker[1], poker[2]);
    }
    
    /**
     * 翻开一张转牌
     * @param msg
     */
    private void setTurnInfo(String msg) {
        String lines[] = msg.split("\n");
        Poker poker = null;
        String items[] = lines[0].split(" ");
        poker = this.convertToPoker(items[0], items[1]);
        this.ai.addTurnPoker(poker);
    }
    
    /**
     * 将字符串表示的牌转换成Poker
     * @param color
     * @param val
     * @return
     */
    private Poker convertToPoker(String color, String val) {
        Poker poker = null;
        if (color.equals("SPADES"))
            poker = new Poker(Color.SPADES, convertToValue(val));
        else if (color.equals("HEARTS"))
            poker = new Poker(Color.HEARTS, convertToValue(val));
        else if (color.equals("CLUBS"))
            poker = new Poker(Color.CLUBS, convertToValue(val));
        else if (color.equals("DIAMONDS"))
            poker = new Poker(Color.DIAMONDS, convertToValue(val));
        return poker;
    }
    
    /**
     * 翻开一张河牌
     * @param msg
     */
    private void setRiverInfo(String msg) {
        String lines[] = msg.split("\n");
        Poker poker = null;
        String items[] = lines[0].split(" ");
        poker = this.convertToPoker(items[0], items[1]);
        
        this.ai.addRiverPoker(poker);
    }
    
    /**
     * 将扑克牌大小转换成数字
     * @param value
     * @return
     */
    private int convertToValue(String value) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("J", "11");
        map.put("Q", "12");
        map.put("K", "13");
        map.put("A", "14");
        if (map.containsKey(value)) {
            value = map.get(value);
        }
        return Integer.parseInt(value);
    }
    
    /**
     * 收到询问并作出回应
     * @param msg
     */
    private void inquireAndAction(String msg) {
        String lines[] = msg.split("\n");
        ArrayList<BetState> states = new ArrayList<BetState>();
        int totalPot = 0;
        for (String line: lines) {
            if (line.startsWith("total pot")) {
                String items[] = line.split(":");
                totalPot = Integer.parseInt(items[1].substring(1, 
                        items[1].length() - 1));
                continue;
            }
            if (line.length() > 3) {
                String items[] = line.split(" ");
                BetState state = new BetState(items[0], 
                        Integer.parseInt(items[1]), Integer.parseInt(items[2]),
                        Integer.parseInt(items[3]), items[4]);
                if (!state.getAction().equals("fold"))
                    states.add(state);
            }
        }
        this.ai.addBetStates(states);
        
        // 根据当前加注状态思考对策
        String action = "";
        switch (this.ai.getPublicPokers().size()) {
        case 0:
            action = this.ai.thinkAfterHold(states);
            break;
        case 3:
            action = this.ai.thinkAfterFlop(states);
            break;
        case 4:
            action = this.ai.thinkAfterTurn(states);
            break;
        case 5:
            action = this.ai.thinkAfterRiver(states);
            break;
        }
        
        this.sendData(action + " \n");
    }
    
    private void showdown(String msg) {
        String lines[] = msg.split("\n");
        HashMap<String, ArrayList<Poker>> holds = new HashMap<String, 
                ArrayList<Poker>>();
        
        for (String line: lines) {
            if (line.indexOf(":") != -1) {
                String items[] = (line.split(": "))[1].split(" ");
                String id = items[0];
                ArrayList<Poker> pokers = new ArrayList<Poker>();
                pokers.add(this.convertToPoker(items[1], items[2]));
                pokers.add(this.convertToPoker(items[3], items[4]));
                holds.put(id, pokers);
            }
        }
        this.ai.parseOtherPlayerInfo(holds);
    }
    
    private void potwin(String msg) {
    }
    
    /**
     * 处理通知信息，保存所有玩家的押注状态
     * @param msg
     */
    private void setNotifyInfo(String msg) {
        String lines[] = msg.split("\n");
        ArrayList<BetState> states = new ArrayList<BetState>();
        int totalPot = 0;
        for (String line: lines) {
            if (line.startsWith("total pot")) {
                String items[] = line.split(":");
                totalPot = Integer.parseInt(items[1].substring(1, 
                        items[1].length() - 1));
                continue;
            }
            if (line.length() > 3) {
                String items[] = line.split(" ");
                BetState state = new BetState(items[0], 
                        Integer.parseInt(items[1]), Integer.parseInt(items[2]),
                        Integer.parseInt(items[3]), items[4]);
                if (!state.getAction().equals("fold"))
                    states.add(state);
            }
        }
        this.ai.addBetStates(states);
    }
    
    /**
     * 向服务端发送数据
     * @param data
     */
    private void sendData(String data) {
        this.output.println(data);
        this.output.flush();

        System.out.println("To server: " + data);
    }
    
    /**
     * 从服务端读取数据
     * @return
     * @throws IOException
     */
    private ArrayList<String> readData() throws IOException {
        ArrayList<String> inLines = new ArrayList<String>();
        String firstLine = this.input.readLine();
        int ind = firstLine.indexOf("/");
        if (ind == -1) {
            int indexOfSpace = firstLine.indexOf(" ");
            inLines.add(firstLine.substring(0, indexOfSpace));
            return inLines;
        }
        else {
            String token = firstLine.substring(0, ind);
            String endFlag = "/" + token;
            StringBuffer buffer = new StringBuffer();
            while (true) {
                String line = this.input.readLine();
                if (line.startsWith(endFlag))
                    break;
                buffer.append(line + "\n");
            }
            inLines.add(token);
            inLines.add(buffer.toString());
            return inLines;
        }
    }
}
